#pragma once

#include <ctime>
#include <cstdint>

enum class LOG_LEVEL : std::uint8_t{
    NORM = 0,
    DBG,
    WARN,
    ERROR,
    FATAL
};

#define CUR_LOG_LEVEL LOG_LEVEL::NORM

static inline const char *LOG_LEVEL_STR(LOG_LEVEL log_level){
    switch(log_level){
        case LOG_LEVEL::NORM:       return "NORM";
        case LOG_LEVEL::DBG:        return "DBG";
        case LOG_LEVEL::WARN:       return "WARN";
        case LOG_LEVEL::ERROR:      return "ERROR";
        case LOG_LEVEL::FATAL:      return "FATAL";
        default:                    return "UNKNOWN";
    }
}

#define LOG(log_level, format, ...) do{                                     \
    if(log_level < CUR_LOG_LEVEL) break;                                    \
    std::time_t t = std::time(nullptr);                                     \
    std::tm *tm_ptr = std::localtime(&t);                                   \
    char buf[64];                                                           \
    strftime(buf, 64, "%Y-%m-%d %H:%M:%S", tm_ptr);                         \
    fprintf(stderr, "[%s] [%s] %s:%d " format ,                             \
        buf, LOG_LEVEL_STR(log_level), __FILE__, __LINE__, ##__VA_ARGS__);  \
}while(0)

#define NORM_LOG(format, ...) LOG(LOG_LEVEL::NORM, format, ##__VA_ARGS__)
#define DBG_LOG(format, ...) LOG(LOG_LEVEL::DBG, format, ##__VA_ARGS__)
#define WARN_LOG(format, ...) LOG(LOG_LEVEL::WARN, format, ##__VA_ARGS__)
#define ERROR_LOG(format, ...) LOG(LOG_LEVEL::ERROR, format, ##__VA_ARGS__)
#define FATAL_LOG(format, ...) LOG(LOG_LEVEL::FATAL, format, ##__VA_ARGS__)